﻿using System.Diagnostics;
using System.Windows;
using System.Windows.Media.Media3D;
using HalconDotNet;
using MachineVision.Core.Extensions;
using MachineVision.Core.TemplateMatch.Shared;
using Prism.Mvvm;
#nullable disable
namespace MachineVision.Core.TemplateMatch.NccModel
{
    public class NccModelService : BindableBase, ITemplateMatchService
    {
        public NccModelService()
        {
            Roi = new ROIParameter();
            Info = new()
            {
                Name = "find_ncc_model",
                Description = "Find the best matches of an NCC model in an image",
                Parameters = new List<MethodParameter>()
                {
                    new MethodParameter()
                    {
                        Name = "Image",
                        Description = "Input image in which the model should be found."
                    },
                    new MethodParameter()
                    {
                        Name = "ModelID",
                        Description = "Handle of the model."
                    },
                    new MethodParameter()
                    {
                        Name = "AngleStart",
                        Description = "Smallest rotation of the model."
                    },
                    new MethodParameter()
                    {
                        Name = "AngleExtent",
                        Description = "Extent of the rotation angles."
                    },
                    new MethodParameter()
                    {
                        Name = "MinScore",
                        Description = "Minimum score of the instances of the model to be found."
                    },
                    new MethodParameter()
                    {
                        Name = "NumMatches",
                        Description =
                            "Number of instances of the model to be found (or 0 for all matches)."
                    },
                    new MethodParameter()
                    {
                        Name = "MaxOverlap",
                        Description = "Maximum overlap of the instances of the model to be found."
                    },
                    new MethodParameter()
                    {
                        Name = "SubPixel",
                        Description = "Subpixel accuracy if not equal to 'none'."
                    },
                    new MethodParameter()
                    {
                        Name = "NumLevels",
                        Description =
                            "Number of pyramid levels used in the matching (and lowest pyramid level to use if |NumLevels| = 2)."
                    },
                    new MethodParameter()
                    {
                        Name = "Row",
                        Description = "Row coordinate of the found instances of the model."
                    },
                    new MethodParameter()
                    {
                        Name = "Column",
                        Description = "Column coordinate of the found instances of the model."
                    },
                    new MethodParameter()
                    {
                        Name = "Angle",
                        Description = "Rotation angle of the found instances of the model."
                    },
                    new MethodParameter()
                    {
                        Name = "Score",
                        Description = "Score of the found instances of the model."
                    }
                },
                Preducessors = new List<string>()
                {
                    "create_ncc_model",
                    "write_ncc_model",
                    "read_ncc_model"
                }
            };
            TemplateParameter = new();
            RunParameter = new();
            Setting = new MatchResultSetting();
            MatchResult = new MatchResult();
        }

        /// <summary>
        /// 模板ID
        /// </summary>
        private HTuple ModelId;
        private HTuple Row;
        private HTuple Column;
        private HTuple Angle;
        private HTuple Score;
        private NccModelInputParameter templateParameter;
        private HWindow window;

        public HWindow Window
        {
            get { return window; }
            set
            {
                window = value;
                RaisePropertyChanged();
            }
        }

        /// <summary>
        /// 形状匹配模板参数
        /// </summary>
        public NccModelInputParameter TemplateParameter
        {
            get { return templateParameter; }
            set
            {
                templateParameter = value;
                RaisePropertyChanged();
            }
        }
        private NccModelRunParameter runParameter;

        /// <summary>
        /// 形状匹配运行参数
        /// </summary>
        public NccModelRunParameter RunParameter
        {
            get { return runParameter; }
            set
            {
                runParameter = value;
                RaisePropertyChanged();
            }
        }
        private MatchResult matchResult;

        public MatchResult MatchResult
        {
            get { return matchResult; }
            set
            {
                matchResult = value;
                RaisePropertyChanged();
            }
        }
        public MethodInfo Info { get; set; }
        public ROIParameter Roi { get; set; }
        public MatchResultSetting Setting { get; set; }

        public Task CreateTemplateAsync(HObject image, HObject hobject)
        {
            if (image == null)
                throw new Exception("不正确的图像格式");
            if (hobject == null)
                throw new Exception("未创建匹配模板");
            HObject imageReduced;
            HOperatorSet.GenEmptyObj(out imageReduced);
            //減少域
            HOperatorSet.ReduceDomain(image, hobject, out imageReduced);
            HOperatorSet.CreateNccModel(
                imageReduced.CropDomain().Rgb1ToGray(),
                TemplateParameter.NumLevels,
                TemplateParameter.AngleStart,
                TemplateParameter.AngleExtent,
                TemplateParameter.AngleStep,
                TemplateParameter.Metric,
                out ModelId
            );
            return Task.CompletedTask;
        }

        public double GetRunTimeSpan(Action action)
        {
            Stopwatch stopwatch = Stopwatch.StartNew();
            action();
            stopwatch.Stop();
            return stopwatch.ElapsedMilliseconds;
        }

        public HObject GetNccModelContours(
            HTuple hv_ModelID,
            HTuple hv_Row,
            HTuple hv_Column,
            HTuple hv_Angle,
            HTuple hv_Model
        )
        {
            HObject ho_ModelRegion = null,
                ho_ModelContours = null;
            HObject ho_ContoursAffinTrans = null,
                ho_Cross = null;
            HTuple hv_NumMatches = new HTuple(),
                hv_Index = new HTuple();
            HTuple hv_Match = new HTuple(),
                hv_HomMat2DIdentity = new HTuple();
            HTuple hv_HomMat2DRotate = new HTuple(),
                hv_HomMat2DTranslate = new HTuple();
            HTuple hv_RowTrans = new HTuple(),
                hv_ColTrans = new HTuple();
            HTuple hv_Model_COPY_INP_TMP = new HTuple(hv_Model);
            HOperatorSet.GenEmptyObj(out ho_ModelRegion);
            HOperatorSet.GenEmptyObj(out ho_ModelContours);
            HOperatorSet.GenEmptyObj(out ho_ContoursAffinTrans);
            HOperatorSet.GenEmptyObj(out ho_Cross);
            hv_NumMatches.Dispose();
            using (HDevDisposeHelper dh = new HDevDisposeHelper())
            {
                hv_NumMatches = new HTuple(hv_Row.TupleLength());
            }
            if ((int)(new HTuple(hv_NumMatches.TupleGreater(0))) != 0)
            {
                if (
                    (int)(
                        new HTuple((new HTuple(hv_Model_COPY_INP_TMP.TupleLength())).TupleEqual(0))
                    ) != 0
                )
                {
                    hv_Model_COPY_INP_TMP.Dispose();
                    HOperatorSet.TupleGenConst(hv_NumMatches, 0, out hv_Model_COPY_INP_TMP);
                }
                else if (
                    (int)(
                        new HTuple((new HTuple(hv_Model_COPY_INP_TMP.TupleLength())).TupleEqual(1))
                    ) != 0
                )
                {
                    {
                        HTuple ExpTmpOutVar_0;
                        HOperatorSet.TupleGenConst(
                            hv_NumMatches,
                            hv_Model_COPY_INP_TMP,
                            out ExpTmpOutVar_0
                        );
                        hv_Model_COPY_INP_TMP.Dispose();
                        hv_Model_COPY_INP_TMP = ExpTmpOutVar_0;
                    }
                }
                for (
                    hv_Index = 0;
                    (int)hv_Index <= (int)((new HTuple(hv_ModelID.TupleLength())) - 1);
                    hv_Index = (int)hv_Index + 1
                )
                {
                    using (HDevDisposeHelper dh = new HDevDisposeHelper())
                    {
                        ho_ModelRegion.Dispose();
                        HOperatorSet.GetNccModelRegion(
                            out ho_ModelRegion,
                            hv_ModelID.TupleSelect(hv_Index)
                        );
                    }
                    ho_ModelContours.Dispose();
                    HOperatorSet.GenContourRegionXld(
                        ho_ModelRegion,
                        out ho_ModelContours,
                        "border_holes"
                    );
                    HTuple end_val13 = hv_NumMatches - 1;
                    HTuple step_val13 = 1;
                    for (
                        hv_Match = 0;
                        hv_Match.Continue(end_val13, step_val13);
                        hv_Match = hv_Match.TupleAdd(step_val13)
                    )
                    {
                        if (
                            (int)(
                                new HTuple(
                                    hv_Index.TupleEqual(hv_Model_COPY_INP_TMP.TupleSelect(hv_Match))
                                )
                            ) != 0
                        )
                        {
                            hv_HomMat2DIdentity.Dispose();
                            HOperatorSet.HomMat2dIdentity(out hv_HomMat2DIdentity);
                            using (HDevDisposeHelper dh = new HDevDisposeHelper())
                            {
                                hv_HomMat2DRotate.Dispose();
                                HOperatorSet.HomMat2dRotate(
                                    hv_HomMat2DIdentity,
                                    hv_Angle.TupleSelect(hv_Match),
                                    0,
                                    0,
                                    out hv_HomMat2DRotate
                                );
                            }
                            using (HDevDisposeHelper dh = new HDevDisposeHelper())
                            {
                                hv_HomMat2DTranslate.Dispose();
                                HOperatorSet.HomMat2dTranslate(
                                    hv_HomMat2DRotate,
                                    hv_Row.TupleSelect(hv_Match),
                                    hv_Column.TupleSelect(hv_Match),
                                    out hv_HomMat2DTranslate
                                );
                            }
                            ho_ContoursAffinTrans.Dispose();
                            HOperatorSet.AffineTransContourXld(
                                ho_ModelContours,
                                out ho_ContoursAffinTrans,
                                hv_HomMat2DTranslate
                            );

                            //  ？？？
                            //HOperatorSet.DispObj(ho_ContoursAffinTrans, Window);
                            //hv_RowTrans.Dispose();
                            //hv_ColTrans.Dispose();
                            //HOperatorSet.AffineTransPixel(
                            //    hv_HomMat2DTranslate,
                            //    0,
                            //    0,
                            //    out hv_RowTrans,
                            //    out hv_ColTrans
                            //);
                            //using (HDevDisposeHelper dh = new HDevDisposeHelper())
                            //{
                            //    ho_Cross.Dispose();
                            //    HOperatorSet.GenCrossContourXld(
                            //        out ho_Cross,
                            //        hv_RowTrans,
                            //        hv_ColTrans,
                            //        6,
                            //        hv_Angle.TupleSelect(hv_Match)
                            //    );
                            //}
                            //HOperatorSet.DispObj(ho_Cross, Window);
                        }
                    }
                }
            }
            ho_ModelRegion.Dispose();
            ho_ModelContours.Dispose();
            //ho_ContoursAffinTrans.Dispose();
            ho_Cross.Dispose();

            hv_Model_COPY_INP_TMP.Dispose();
            hv_NumMatches.Dispose();
            hv_Index.Dispose();
            hv_Match.Dispose();
            hv_HomMat2DIdentity.Dispose();
            hv_HomMat2DRotate.Dispose();
            hv_HomMat2DTranslate.Dispose();
            hv_RowTrans.Dispose();
            hv_ColTrans.Dispose();
            return ho_ContoursAffinTrans;
        }

        public void Run(HObject image)
        {
            MatchResult result = new MatchResult();
            if (image == null)
                throw new Exception("图像格式不正确");
            if (ModelId == default(HTuple))
                throw new Exception("未创建匹配模板");
            var timespan = GetRunTimeSpan(() =>
            {
                HObject reduceimg;
                HOperatorSet.GenEmptyObj(out reduceimg);
                //该扩展方法用于判断是否设定检测区域，如果参数有效则返回需要裁剪的区域，否则返回本身
                reduceimg = image.ReduceDomain(Roi).Rgb1ToGray(); //相似性匹配需要转换成灰度图像
                //开始匹配
                HOperatorSet.FindNccModel(
                    reduceimg,
                    ModelId,
                    TemplateParameter.AngleStart,
                    TemplateParameter.AngleExtent,
                    RunParameter.MinScore,
                    RunParameter.NumMatches,
                    RunParameter.MaxOverlop,
                    RunParameter.SubPixel,
                    (int)RunParameter.NumLevels, //此处参数类型需要转换成Int类型
                    out Row,
                    out Column,
                    out Angle,
                    out Score
                );
            });

            for (int i = 0; i < Score.Length; i++)
            {
                var row1 = Math.Round(Row.DArr[i], 3);
                var col1 = Math.Round(Column.DArr[i], 3);
                var ang1 = Math.Round(Angle.DArr[i], 3);
                var score1 = Math.Round(Score.DArr[i], 3);
                var nccmodelcontours = GetNccModelContours(
                    ModelId,
                    Row.DArr[i],
                    Column.DArr[i],
                    Angle.DArr[i],
                    0
                );
                result.Results.Add(
                    new TemplateMatchResult()
                    {
                        Index = i + 1,
                        Row = row1,
                        Column = col1,
                        Angle = ang1,
                        Score = score1,
                        Contours = nccmodelcontours,
                    }
                );
                if (Setting.IsShowCenter)
                    HOperatorSet.DispCross(Window, row1, col1, 20, score1);
                if (Setting.IsShowMatchRange)
                {
                    Window.DispObj(nccmodelcontours);
                }
                if (Setting.IsShowDisplayText)
                {
                    Window.SetString(
                        $"{row1} : {col1} : {score1}",
                        "image",
                        row1,
                        col1,
                        "black",
                        "true"
                    );
                }
            }
            result.TimeSpan = timespan;
            result.Message =
                $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}：匹配耗时:{timespan}ms,"
                + $"{(result.Results.Count > 0 ? $"匹配个数:{result.Results.Count}" : "未匹配相关数据")}";
            MatchResult = result;
            //return result;
        }
    }
}
